package org.wsff.tools.state.core;

import lombok.Getter;
import lombok.Setter;

import java.util.List;

import org.wsff.tools.state.event.EventListenerSupport;
import org.wsff.tools.state.event.EventMulticaster;

/**
 * abstract state manager
 *
 * @author ryan
 * @version Id: AbstractStateManager.java, v 0.1 2022-03-24 16:00 ryan Exp $$
 */
public abstract class AbstractStateManager implements StateManager, LifeCycle {

    @Getter
    @Setter
    private StateTransferProvider stateTransferProvider;

    @Getter
    @Setter
    private EventMulticaster      eventMulticaster;

    /**
     * set state
     *
     * @param context state context
     * @param to to state
     */
    @Override
    public void setState(StateContext context, State to) {
        StateTransfer transfer = getStateTransferProvider().get(context.from(), to);
        if (transfer == null) {
            throw new StateChangeException("not support state transfer form-to: " + context.from().id() + "-" + to.id());
        }
        // state change
        boolean success = updateState(context, to);
        if (!success) {
            throw new StateChangeException("update state failure form-to: " + context.from().id() + "-" + to.id());
        }

        // before push state event
        beforePushStateEvent(context);

        // push event
        pushStateEvent(context, transfer);

        // after push state event
        afterPushStateEvent(context);

    }

    /**
     * before push state event
     *
     * @param context context
     */
    protected void beforePushStateEvent(StateContext context) {

    }

    /**
     * after push state event
     *
     * @param context context
     */
    protected void afterPushStateEvent(StateContext context) {

    }

    /**
     * push state event
     *
     * @param context state context
     * @param transfer state transfer
     */
    protected void pushStateEvent(StateContext context, StateTransfer transfer) {
        boolean sync = transfer.sync(context);
        StateEvent event = transfer.event(context);

        // push event
        eventMulticaster.multicastEvent(event, sync);
    }

    /**
     * update state
     * 
     * @param context context
     * @param to to State
     * @return is success         
     */
    protected abstract boolean updateState(StateContext context, State to);

    /**
     * init when the container is started
     */
    @Override
    public void init() {
        initEventMulticaster();
        initStateTransferProvider();
    }

    /**
     * init StateTransferProvider
     */
    protected void initStateTransferProvider() {
    }

    /**
     * get all state event listener
     *
     * @return state listener
     */
    protected abstract List<StateEventListener> getAllStateEventListeners();

    /**
     * init EventMulticaster
     */
    protected void initEventMulticaster() {
        List<StateEventListener> listeners = getAllStateEventListeners();
        for (StateEventListener listener : listeners) {
            getEventListenerSupport().addListener(listener);
        }
    }

    /**
     * Gets event listener support.
     */
    protected EventListenerSupport getEventListenerSupport() {
        return (EventListenerSupport) eventMulticaster;
    }

    /**
     * destroy when the container is stopped
     */
    @Override
    public void destroy() {
        getEventListenerSupport().removeAllListeners();
    }
}
